/********************************************************
 ** Nombre: GestorGeneradorHorarios.java                *
 ** Version: 1.0.0                                      *
 ** Diagrama: GAPI v4.5.6                               *
 ** Autor: Alberto Castro, Alonso Orozco                *
 ** Creado: 11-abr-2013                                 *
 ** Modificado: 3-may-2013                              *
 ********************************************************/
package Servicios;

import DAO.DAOAsignaturaOfertaAcademica;
import DAO.DAOAsignaturaOptativa;
import DAO.DAOAsignaturasAlumno;
import DAO.DAOAsignaturasOptativasAlumno;
import DAO.DAOInformacionAlumno;
import DAO.DAOPreguntasEHE;
import DAO.DAOProgramaEducativo;
import DAO.DAORelacionAlumnoProgramaEducativo;
import DAO.DAORelacionesPreguntas;
import DAO.DAORequerimientoAcademico;
import DAO.DAORespuestasEDD;
import Dominio.Asignatura;
import Dominio.AsignaturaOfertaAcademica;
import Dominio.AsignaturaOfertaAcademicaPonderada;
import Dominio.FragmentoHorario;
import Dominio.OpcionHorario;
import Dominio.PreguntaEDD;
import Dominio.PreguntaEHE;
import Dominio.RelacionRespuestaEHEPreguntaEDD;
import Dominio.RequerimientoAcademico;
import Dominio.RespuestaEHE;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Gestiona los procedimientos de generación de opciones de horario para el alumno.
 * @author Alberto, Alonso
 */
public class GestorGeneradorHorarios extends HttpServlet {

    /**
     * Recibe la petición e inicia el proceso de generación de opciones de horario con los parámetros recibidos.
     * @param request - petición, incluye el nombre de usuario del alumno y el número de asignaturas que desea se consideren
     * para las opciones de horario generadas.
     * @param response - respuesta
     * @throws ServletException Si ocurre un problema con el servlet.
     * @throws IOException Si ocurre un problema de I/O.
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();

        HttpSession sesion = request.getSession();
        String nombreUsuario = sesion.getAttribute("nombreUsuario").toString();
        Object turnoAsistenciaSesionObjeto = sesion.getAttribute("turnoSesion");
        ArrayList<AsignaturaOfertaAcademicaPonderada> arregloAsignaturas = (ArrayList<AsignaturaOfertaAcademicaPonderada>) sesion.getAttribute("asignaturas");

        String numeroMaterias = request.getParameter("numeroMaterias");
        String turnoDeAsistencia = request.getParameter("turnoParametro");

        if (nombreUsuario != null && numeroMaterias != null && turnoDeAsistencia != null) {                       
            
            int numeroDeAsignaturas = Integer.parseInt(numeroMaterias);            
            
            int claveInformacionAlumno = obtenerClaveInformacionAlumno(nombreUsuario);                       
            int clavePE = obtenerClavePE(claveInformacionAlumno);
            String abreviaturaPE = obtenerAbreviaturaPE(clavePE);           
           
            
            DAORelacionAlumnoProgramaEducativo daoRelacionAlumnoPE = new DAORelacionAlumnoProgramaEducativo();            
            int claveRelacionAlumnoPE = daoRelacionAlumnoPE.obtenerClaveRelacionAlumnoPE(claveInformacionAlumno);
            daoRelacionAlumnoPE.cerrarConexionARelacionAlumnoProgramaEducativo();
            
            int numeroDeAsignaturasAlumno = 0;
            DAOAsignaturasAlumno daoAsignaturasAlumno = new DAOAsignaturasAlumno();
            numeroDeAsignaturasAlumno += daoAsignaturasAlumno.obtenerNumeroDeAsignaturasDelAlumno(claveRelacionAlumnoPE);
            daoAsignaturasAlumno.cerrarConexionAAsignaturaAlumno();
            
            DAOAsignaturasOptativasAlumno daoAsignaturasOptativasAlumno = new DAOAsignaturasOptativasAlumno();
            numeroDeAsignaturasAlumno += daoAsignaturasOptativasAlumno.obtenerNumeroDeAsignaturasOptativasDelAlumno(claveRelacionAlumnoPE);
            daoAsignaturasOptativasAlumno.cerrarConexionAAsignaturaOptativaAlumno();
            
            if(numeroDeAsignaturasAlumno > 0) {
                                                
                if (arregloAsignaturas == null) {
                    
                    ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOfertadas = new ArrayList<AsignaturaOfertaAcademica>();

                    if(turnoDeAsistencia.equals(TURNO_MEZCLADO)) {
                        listaAsignaturasOfertadas = obtenerAsignaturasOfertadas(abreviaturaPE);            
                    }
                    else if(turnoDeAsistencia.equals(TURNO_MATUTINO)) {
                        listaAsignaturasOfertadas = obtenerAsignaturasOfertadasTurnoMatutino(abreviaturaPE); //
                    }
                    else if(turnoDeAsistencia.equals(TURNO_VESPERTINO)) {
                        listaAsignaturasOfertadas = obtenerAsignaturasOfertadasTurnoVespertino(abreviaturaPE); //
                    }

                    if(listaAsignaturasOfertadas.size() > 0) {

                        arregloAsignaturas = generarAsignaturasPonderadas(request, response, listaAsignaturasOfertadas, claveInformacionAlumno, clavePE, nombreUsuario, claveRelacionAlumnoPE);
                        ArrayList<OpcionHorario> opcionesDeHorario = obtenerOpcionesDeHorario(arregloAsignaturas, numeroDeAsignaturas);

                        imprimirOpcionesDeHorario(opcionesDeHorario, out);
                    } 
                    else {
                        String respuesta = "<h1>En estos momentos no existe una oferta académica en el sistema o no existen asignaturas ofertadas para su programa educativo, por lo que no puede generar sus opciones de horarios</h1>";
                        out.println(respuesta);
                    }                                                

                } else {
                        
                    if(turnoAsistenciaSesionObjeto == null) {                       
                        
                        sesion.setAttribute("turnoSesion", turnoDeAsistencia);

                        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOfertadas = new ArrayList<AsignaturaOfertaAcademica>();

                        if(turnoDeAsistencia.equals(TURNO_MEZCLADO)) {
                            listaAsignaturasOfertadas = obtenerAsignaturasOfertadas(abreviaturaPE);            
                        }
                        else if(turnoDeAsistencia.equals(TURNO_MATUTINO)) {
                            listaAsignaturasOfertadas = obtenerAsignaturasOfertadasTurnoMatutino(abreviaturaPE); //
                        }
                        else if(turnoDeAsistencia.equals(TURNO_VESPERTINO)) {
                            listaAsignaturasOfertadas = obtenerAsignaturasOfertadasTurnoVespertino(abreviaturaPE); //
                        }
                        
                        if(listaAsignaturasOfertadas.size() > 0) {

                            arregloAsignaturas = generarAsignaturasPonderadas(request, response, listaAsignaturasOfertadas, claveInformacionAlumno, clavePE, nombreUsuario, claveRelacionAlumnoPE);
                            ArrayList<OpcionHorario> opcionesDeHorario = obtenerOpcionesDeHorario(arregloAsignaturas, numeroDeAsignaturas);

                            imprimirOpcionesDeHorario(opcionesDeHorario, out);
                        } 
                        else {
                            String respuesta = "<h1>En estos momentos no existe una oferta académica en el sistema o no existen asignaturas ofertadas para su programa educativo, por lo que no puede generar sus opciones de horarios</h1>";
                            out.println(respuesta);
                        }         
                    }
                    else {
                        
                        String turnoSesion = turnoAsistenciaSesionObjeto.toString();
                        
                        if(turnoSesion.equals(turnoDeAsistencia)) {                                                        
                            
                            ArrayList<OpcionHorario> opcionesDeHorario = obtenerOpcionesDeHorario(arregloAsignaturas, numeroDeAsignaturas);

                            imprimirOpcionesDeHorario(opcionesDeHorario, out);
                        }
                        else {
                            sesion.setAttribute("turnoSesion", turnoDeAsistencia);

                            ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOfertadas = new ArrayList<AsignaturaOfertaAcademica>();

                            if(turnoDeAsistencia.equals(TURNO_MEZCLADO)) {
                                listaAsignaturasOfertadas = obtenerAsignaturasOfertadas(abreviaturaPE);            
                            }
                            else if(turnoDeAsistencia.equals(TURNO_MATUTINO)) {
                                listaAsignaturasOfertadas = obtenerAsignaturasOfertadasTurnoMatutino(abreviaturaPE); //
                            }
                            else if(turnoDeAsistencia.equals(TURNO_VESPERTINO)) {
                                listaAsignaturasOfertadas = obtenerAsignaturasOfertadasTurnoVespertino(abreviaturaPE); //
                            }

                            if(listaAsignaturasOfertadas.size() > 0) {

                                arregloAsignaturas = generarAsignaturasPonderadas(request, response, listaAsignaturasOfertadas, claveInformacionAlumno, clavePE, nombreUsuario, claveRelacionAlumnoPE);
                                ArrayList<OpcionHorario> opcionesDeHorario = obtenerOpcionesDeHorario(arregloAsignaturas, numeroDeAsignaturas);

                                imprimirOpcionesDeHorario(opcionesDeHorario, out);
                            } 
                            else {
                                String respuesta = "<h1>En estos momentos no existe una oferta académica en el sistema o no existen asignaturas ofertadas para su programa educativo, por lo que no puede generar sus opciones de horarios</h1>";
                                out.println(respuesta);
                            }         
                        }
                    }                  
                        

                    }                
            }
            else {
                String respuesta = "<h1>Para generar sus opciones de horario debe gestionar antes el estado de sus asignaturas</h1>";
                out.println(respuesta);
            }
        } else {
            String respuesta = "Ocurrio un error al tratar de generar las opciones de horarios";
            out.println(respuesta);
        }
    }

    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
     * Handles the HTTP
     * <code>GET</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Handles the HTTP
     * <code>POST</code> method.
     *
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /**
     * Returns a short description of the servlet.
     *
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>

    /**
     * Genera un <code>ArrayList</code> de AsignaturaOfertaAcademicaPonderada
     * tomando las asignaturas de la oferta académica vigente que el alumno puede cursar y asignándoles
     * una ponderación con base en los criterios de conveniencia para el
     * perfil académico del alumno.
     * @param request - petición
     * @param response - respuesta
     * @param listaAsignaturasOfertadas Conjunto de asignaturas ofertadas
     * @param claveInformacionAlumno Clave del registro de información del alumno en la base de datos.
     * @param clavePE Clave del Programa Educativo del alumno.
     * @param nombreUsuario Nombre de usuario del alumno.
     * @param claveRelacionAlumnoPE Clave del registro de la relación Alumno-Programa Educativo en la base de datos.
     * @return ArrayList de asignaturas de la oferta académica con ponderaciones de conveniencia asignadas.
     */
    private ArrayList<AsignaturaOfertaAcademicaPonderada> generarAsignaturasPonderadas(HttpServletRequest request, HttpServletResponse response, ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOfertadas, int claveInformacionAlumno, int clavePE, String nombreUsuario, int claveRelacionAlumnoPE) {        
        
        System.out.println("Obteniendo asignaturas normales aprobadas");
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasNormalesAprobadas = obtenerAsignaturasNormalesPorEstadoOfertada(listaAsignaturasOfertadas, claveInformacionAlumno, clavePE, ESTADO_APROBADA);
        System.out.println("Obteniendo asignaturas normales recursables");
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasNormalesRecursables = obtenerAsignaturasNormalesPorEstadoOfertada(listaAsignaturasOfertadas, claveInformacionAlumno, clavePE, ESTADO_RECURSABLE);
        System.out.println("Obteniendo asignaturas normales cursables sin recursables");
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasNormalesCursablesSinRecursables = obtenerAsignaturasNormalesPorEstadoOfertada(listaAsignaturasOfertadas, claveInformacionAlumno, clavePE, ESTADO_CURSABLE_SIN_RECURSABLE);

        System.out.println("Obteniendo asignaturas optativas aprobadas");
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOptativasAprobadas = obtenerAsignaturasOptativasPorEstadoOfertada(listaAsignaturasOfertadas,  claveInformacionAlumno, clavePE, ESTADO_APROBADA);        
        System.out.println("Obteniendo asignaturas optativas recursables");
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOptativasRecursables = obtenerAsignaturasOptativasPorEstadoOfertada(listaAsignaturasOfertadas, claveInformacionAlumno, clavePE, ESTADO_RECURSABLE);
        System.out.println("Obteniendo asignaturas optativas cursables sin recursables");
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOptativasCursablesSinRecursables = obtenerAsignaturasOptativasPorEstadoOfertada(listaAsignaturasOfertadas, claveInformacionAlumno, clavePE, ESTADO_CURSABLE_SIN_RECURSABLE);

        ArrayList<AsignaturaOfertaAcademicaPonderada> arregloAsignaturasOrdenadas = new ArrayList<AsignaturaOfertaAcademicaPonderada>();

        if (obtenerNumeroDeAsignaturasOptativasAprobadas(listaAsignaturasOptativasAprobadas) < 6) {

            System.out.println("Obteniendo todas las asignaturas aprobadas");
            ArrayList<AsignaturaOfertaAcademica> listaDeTodasLasAsignaturasAprobadas = obtenerTodasLasAsignaturasAprobadas(listaAsignaturasNormalesAprobadas, listaAsignaturasOptativasAprobadas);

            System.out.println("Obteniendo ponderaciones de asignaturas normales recursables");
            ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasNormalesRecursables = obtenerAsignaturasPonderadasRecursables(listaAsignaturasNormalesRecursables);
            System.out.println("Obteniendo ponderaciones de asignaturas cursables sin recursables");
            ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasNormalesCursablesSinRecursables = obtenerAsignaturasPonderadasNormales(listaAsignaturasNormalesCursablesSinRecursables);
            System.out.println("Obteniendo ponderaciones de asignaturas optativas recursables");
            ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasOptativasRecursables = obtenerAsignaturasPonderadasOptativas(listaAsignaturasOptativasRecursables);
            System.out.println("Obteniendo ponderaciones de asignaturas optativas cursables sin recursables");
            ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasOptativasCursablesSinRecursables = obtenerAsignaturasPonderadasOptativas(listaAsignaturasOptativasCursablesSinRecursables);

            System.out.println("Asignar ponderacion con base en semestre de asignaturas normales recursables");
            asignarPonderacionAAsignaturasPorSemestre(listaAsignaturasPonderadasNormalesRecursables);
            System.out.println("Asignar ponderacion con base en semestre de asignaturas normales cursables sin recursables");
            asignarPonderacionAAsignaturasPorSemestre(listaAsignaturasPonderadasNormalesCursablesSinRecursables);
            System.out.println("Asignar ponderacion con base en semestre de asignaturas optativas recursables");
            asignarPonderacionAAsignaturasPorSemestre(listaAsignaturasPonderadasOptativasRecursables);
            System.out.println("Asignar ponderacion con base en semestre de asignaturas optativas cursables sin recursables");
            asignarPonderacionAAsignaturasPorSemestre(listaAsignaturasPonderadasOptativasCursablesSinRecursables);

            System.out.println("Asignar ponderacion con base en requerimientos de asignaturas normales cursables sin recursables");
            asignarPonderacionesConBaseEnLosRequerimientosAcademicos(listaAsignaturasPonderadasNormalesCursablesSinRecursables, listaDeTodasLasAsignaturasAprobadas, clavePE);
            System.out.println("Asignar ponderacion con base en requerimientos de asignaturas optativas recursables");
            asignarPonderacionesConBaseEnLosRequerimientosAcademicos(listaAsignaturasPonderadasOptativasRecursables, listaDeTodasLasAsignaturasAprobadas, clavePE);
            System.out.println("Asignar ponderacion con base en requerimientos de asignaturas optativas cursables sin recursables");
            asignarPonderacionesConBaseEnLosRequerimientosAcademicos(listaAsignaturasPonderadasOptativasCursablesSinRecursables, listaDeTodasLasAsignaturasAprobadas, clavePE);

            System.out.println("Asignando ponderación con base a las encuestas a las asignaturas normales cursables");
            asignarPonderacionConBaseEnRespuestasDeEncuestas(nombreUsuario,listaAsignaturasPonderadasNormalesCursablesSinRecursables);
            System.out.println("Asignando ponderación con base a las encuestas a las asignaturas optativas cursables");
            asignarPonderacionConBaseEnRespuestasDeEncuestas(nombreUsuario,listaAsignaturasPonderadasOptativasCursablesSinRecursables);
            
            System.out.println("Obteniendo claves de areas de concentracion aprobadas");
            ArrayList<Integer> listaClavesAreasDeConcentracionAprobadas = obtenerClavesDeAreasDeConcentracionAprobadas(listaAsignaturasOptativasAprobadas);
            System.out.println("Asignando ponderación por área de concentración aprobada a optativas recursables");
            asignarPonderacionAOptativaPorAreaDeConcentracion(listaClavesAreasDeConcentracionAprobadas, listaAsignaturasPonderadasOptativasRecursables);
            System.out.println("Asignando ponderación por área de concentración aprobada a optativas cursables");
            asignarPonderacionAOptativaPorAreaDeConcentracion(listaClavesAreasDeConcentracionAprobadas, listaAsignaturasPonderadasOptativasCursablesSinRecursables);
            
            asignarPonderacionPorSemestreDeAlumno(listaAsignaturasPonderadasNormalesRecursables, claveRelacionAlumnoPE);
            asignarPonderacionPorSemestreDeAlumno(listaAsignaturasPonderadasNormalesCursablesSinRecursables, claveRelacionAlumnoPE);
            asignarPonderacionPorSemestreDeAlumno(listaAsignaturasPonderadasOptativasRecursables, claveRelacionAlumnoPE);
            asignarPonderacionPorSemestreDeAlumno(listaAsignaturasPonderadasOptativasCursablesSinRecursables, claveRelacionAlumnoPE);
            
            System.out.println("Uniendo arreglos de asignaturas");
            arregloAsignaturasOrdenadas = unirArreglosDeAsignaturasPonderadas(listaAsignaturasPonderadasNormalesRecursables, listaAsignaturasPonderadasNormalesCursablesSinRecursables, listaAsignaturasPonderadasOptativasRecursables, listaAsignaturasPonderadasOptativasCursablesSinRecursables);
        } else {
            
            System.out.println("Obteniendo todas las asignaturas aprobadas");
            ArrayList<AsignaturaOfertaAcademica> listaDeTodasLasAsignaturasAprobadas = obtenerTodasLasAsignaturasAprobadas(listaAsignaturasNormalesAprobadas, listaAsignaturasOptativasAprobadas);

            System.out.println("Obteniendo ponderaciones de asignaturas normales recursables");
            ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasNormalesRecursables = obtenerAsignaturasPonderadasRecursables(listaAsignaturasNormalesRecursables);
            System.out.println("Obteniendo ponderaciones de asignaturas cursables sin recursables");
            ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasNormalesCursablesSinRecursables = obtenerAsignaturasPonderadasNormales(listaAsignaturasNormalesCursablesSinRecursables);

            System.out.println("Asignar ponderacion con base en semestre de asignaturas normales recursables");
            asignarPonderacionAAsignaturasPorSemestre(listaAsignaturasPonderadasNormalesRecursables);
            System.out.println("Asignar ponderacion con base en semestre de asignaturas normales cursables sin recursables");
            asignarPonderacionAAsignaturasPorSemestre(listaAsignaturasPonderadasNormalesCursablesSinRecursables);

            System.out.println("Asignar ponderacion con base en requerimientos de asignaturas normales cursables sin recursables");
            asignarPonderacionesConBaseEnLosRequerimientosAcademicos(listaAsignaturasPonderadasNormalesCursablesSinRecursables, listaDeTodasLasAsignaturasAprobadas, clavePE);

            System.out.println("Asignando ponderación con base a las encuestas a las asignaturas normales cursables");
            asignarPonderacionConBaseEnRespuestasDeEncuestas(nombreUsuario,listaAsignaturasPonderadasNormalesCursablesSinRecursables);       
            
            asignarPonderacionPorSemestreDeAlumno(listaAsignaturasPonderadasNormalesRecursables, claveRelacionAlumnoPE);
            asignarPonderacionPorSemestreDeAlumno(listaAsignaturasPonderadasNormalesCursablesSinRecursables, claveRelacionAlumnoPE);           
            
            System.out.println("Uniendo arreglos de asignaturas");
            arregloAsignaturasOrdenadas = unirArreglosDeAsignaturasPonderadas(listaAsignaturasPonderadasNormalesRecursables, listaAsignaturasPonderadasNormalesCursablesSinRecursables);
        }

        HttpSession sesion = request.getSession();
        sesion.setAttribute("asignaturas", arregloAsignaturasOrdenadas);

        return arregloAsignaturasOrdenadas;
    }

    /**
     * Obtiene la clave del registro de información del alumno en la base de datos correspondiente al nombre de usuario indicado.
     * @param nombreUsuario Nombre de usuario del  alumno.
     * @return Clave del registro de información del alumno.
     */
    private int obtenerClaveInformacionAlumno(String nombreUsuario) {
        int claveInformacionAlumno = 0;
        DAOInformacionAlumno daoInformacionAlumno = new DAOInformacionAlumno();
        claveInformacionAlumno = daoInformacionAlumno.obtenerClaveInfoAlumno(nombreUsuario);
        daoInformacionAlumno.cerrarConexionADAOInformacionAlumno();
        return claveInformacionAlumno;
    }

    /**
     * Obtiene la clave del Programa Educativo del alumno.
     * @param claveInformacionAlumno Clave del registro de información del alumno en la base de datos.
     * @return La clave del programa Educativo asociado al alumno.
     */
    private int obtenerClavePE(int claveInformacionAlumno) {
        int clavePE = 0;
        DAORelacionAlumnoProgramaEducativo daoRelacionAlumnoPE = new DAORelacionAlumnoProgramaEducativo();
        clavePE = daoRelacionAlumnoPE.obtenerClavePE(claveInformacionAlumno);
        daoRelacionAlumnoPE.cerrarConexionARelacionAlumnoProgramaEducativo();
        return clavePE;
    }
    
    /**
     * Obtiene la abreviatura del programa educativo registrada en la base de datos.
     * @param clavePE Clave del programa Educativo.
     * @return String: la abreviatura del programa educativo solicitado.
     */
    public String obtenerAbreviaturaPE(int clavePE) {
        DAOProgramaEducativo daoPE = new DAOProgramaEducativo();
        String abreviaturaPE = daoPE.obtenerabreviaturaDePE(clavePE);
        daoPE.cerrarconexionAProgramas();
        
        return abreviaturaPE;
    }

    /**
     * Obtiene el listado de las asignaturas ofertadas que corresponden a la retícula del programaeducativo indicado.
     * @param abreviaturaProgramaEducativo Abreviatura del programa educativo.
     * @return ArrayList de AsignaturaOfertaAcademica que corresponden a la retícula académica del programa educativo indicado.
     */
    private ArrayList<AsignaturaOfertaAcademica> obtenerAsignaturasOfertadas(String abreviaturaProgramaEducativo) {
        DAOAsignaturaOfertaAcademica daoAsignaturaOfertaAcademica = new DAOAsignaturaOfertaAcademica();
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOfertadas = daoAsignaturaOfertaAcademica.buscarAsignaturasOfertadasPorPE(abreviaturaProgramaEducativo);
        daoAsignaturaOfertaAcademica.cerrarConexionAAsignaturaOferta();
        
        return listaAsignaturasOfertadas;
    }
    
    /**
     * Obtiene el listado de las asignaturas ofertadas que corresponden a la retícula del programaeducativo indicado para el turno matutino.
     * @param abreviaturaProgramaEducativo Abreviatura del programa educativo.
     * @return ArrayList de AsignaturaOfertaAcademica que corresponden a la retícula académica del programa educativo indicado.
     */
    private ArrayList<AsignaturaOfertaAcademica> obtenerAsignaturasOfertadasTurnoMatutino(String abreviaturaProgramaEducativo) {
        DAOAsignaturaOfertaAcademica daoAsignaturaOfertaAcademica = new DAOAsignaturaOfertaAcademica();
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOfertadas = daoAsignaturaOfertaAcademica.buscarAsignaturasOfertadasPorPEMatutino(abreviaturaProgramaEducativo);
        daoAsignaturaOfertaAcademica.cerrarConexionAAsignaturaOferta();
        
        return listaAsignaturasOfertadas;
    }
    
    /**
     * Obtiene el listado de las asignaturas ofertadas que corresponden a la retícula del programaeducativo indicado para el turno vespertino.
     * @param abreviaturaProgramaEducativo Abreviatura del programa educativo.
     * @return ArrayList de AsignaturaOfertaAcademica que corresponden a la retícula académica del programa educativo indicado.
     */
    private ArrayList<AsignaturaOfertaAcademica> obtenerAsignaturasOfertadasTurnoVespertino(String abreviaturaProgramaEducativo) {
        DAOAsignaturaOfertaAcademica daoAsignaturaOfertaAcademica = new DAOAsignaturaOfertaAcademica();
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOfertadas = daoAsignaturaOfertaAcademica.buscarAsignaturasOfertadasPorPEVeespertino(abreviaturaProgramaEducativo);
        daoAsignaturaOfertaAcademica.cerrarConexionAAsignaturaOferta();
        
        return listaAsignaturasOfertadas;
    }

    /**
     * Método para obtener asignaturas de la oferta según el estado registrado de la asignatura en la información académica del alumno.
     * @param asignaturasOfertadas Conjunto de asignaturas ofertadas.
     * @param claveInfoAlumno Clave del registro de información del alumno en la base de datos.
     * @param claveProgramaEducativo Clave del programa educativo del alumno.
     * @param estado Clave del estado de la asignatura.
     * @return Conjunto de asignaturas de la oferta académica que tienen el estado deseado según la información del alumno.
     */
    private ArrayList<AsignaturaOfertaAcademica> obtenerAsignaturasNormalesPorEstadoOfertada(ArrayList<AsignaturaOfertaAcademica> asignaturasOfertadas, int claveInfoAlumno, int claveProgramaEducativo, int estado) {
        DAOAsignaturasAlumno daoAsignaturasAlumno = new DAOAsignaturasAlumno();
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasPorEstadoOfertada = daoAsignaturasAlumno.obtenerAsignaturasPorEstadoOfertada(asignaturasOfertadas, claveInfoAlumno, claveProgramaEducativo, estado);
        daoAsignaturasAlumno.cerrarConexionAAsignaturaAlumno();

        return listaAsignaturasPorEstadoOfertada;
    }

    /**
     * Método para obtener asignaturas optativas de la oferta según el estado registrado de la asignatura en la información académica del alumno.
     * @param asignaturasOfertadas Conjunto de asignaturas ofertadas.
     * @param claveInfoAlumno Clave del registro de información del alumno en la base de datos.
     * @param claveProgramaEducativo Clave del programa educativo del alumno.
     * @param estado Clave del estado de la asignatura.
     * @return Conjunto de asignaturas optativas de la oferta académica que tienen el estado deseado según la información del alumno.
     */
    private ArrayList<AsignaturaOfertaAcademica> obtenerAsignaturasOptativasPorEstadoOfertada(ArrayList<AsignaturaOfertaAcademica> asignaturasOfertadas, int claveInfoAlumno, int claveProgramaEducativo, int estado) {
        DAOAsignaturasOptativasAlumno daoAsignaturasOptativasAlumno = new DAOAsignaturasOptativasAlumno();
        ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOptativasPorEstadoOfertada = daoAsignaturasOptativasAlumno.obtenerAsignaturasPorEstadoOfertada(asignaturasOfertadas, claveInfoAlumno, claveProgramaEducativo, estado);
        daoAsignaturasOptativasAlumno.cerrarConexionAAsignaturaOptativaAlumno();

        return listaAsignaturasOptativasPorEstadoOfertada;
    }

    /**
     * Conjunta las asignaturas normales y optativas aprobadas del alumno y las devuelve en un solo ArrayList.
     * @param listaAsignaturasNormalesAprobadas ArrayList de Asignaturas aprobadas.
     * @param listaAsignaturasOptativasAprobadas ArrayList de asignaturas optativas aprobadas.
     * @return Conjunto de asignaturas normales y optativas aprobadas.
     */
    private ArrayList<AsignaturaOfertaAcademica> obtenerTodasLasAsignaturasAprobadas(ArrayList<AsignaturaOfertaAcademica> listaAsignaturasNormalesAprobadas, ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOptativasAprobadas) {
        ArrayList<AsignaturaOfertaAcademica> listaTodasLasAsignaturasAprobadas = new ArrayList<AsignaturaOfertaAcademica>();

        listaTodasLasAsignaturasAprobadas.addAll(listaAsignaturasNormalesAprobadas);
        listaTodasLasAsignaturasAprobadas.addAll(listaAsignaturasOptativasAprobadas);

        return listaTodasLasAsignaturasAprobadas;
    }

    /**
     * Asigna la ponderación debida a las asignaturas recursables de la oferta académica.
     * @param listaAsignaturasRecursables Conjunto de asignaturas recursables de la oferta.
     * @return Conjunto de asignaturas ponderadas recursables de la oferta académica.
     */
    private ArrayList<AsignaturaOfertaAcademicaPonderada> obtenerAsignaturasPonderadasRecursables(ArrayList<AsignaturaOfertaAcademica> listaAsignaturasRecursables) {
        ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasRecursablesPonderadas = new ArrayList<AsignaturaOfertaAcademicaPonderada>();

        for (int i = 0; i < listaAsignaturasRecursables.size(); i++) {
            AsignaturaOfertaAcademica asignaturaOfertaAcademica = listaAsignaturasRecursables.get(i);
            AsignaturaOfertaAcademicaPonderada asignaturaPonderada = new AsignaturaOfertaAcademicaPonderada();
            asignaturaPonderada.setAsignaturaOferta(asignaturaOfertaAcademica);
            asignaturaPonderada.setPonderacion(PONDERACION_ASIGNATURA_RECURSABLE);
            listaAsignaturasRecursablesPonderadas.add(asignaturaPonderada);
        }

        return listaAsignaturasRecursablesPonderadas;
    }

    /**
     * Asigna la ponderación correspondiente a las asignaturas de la oferta académica y devuelve el conjunto resultante.
     * @param listaAsignaturasNormales Conjunto de asignaturas de la oferta académica.
     * @return Conjunto de asignaturas de la oferta académica con ponderaciones asignadas.
     */
    private ArrayList<AsignaturaOfertaAcademicaPonderada> obtenerAsignaturasPonderadasNormales(ArrayList<AsignaturaOfertaAcademica> listaAsignaturasNormales) {
        ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasNormalesPonderadas = new ArrayList<AsignaturaOfertaAcademicaPonderada>();

        for (int i = 0; i < listaAsignaturasNormales.size(); i++) {
            AsignaturaOfertaAcademica asignaturaOfertaAcademica = listaAsignaturasNormales.get(i);
            AsignaturaOfertaAcademicaPonderada asignaturaPonderada = new AsignaturaOfertaAcademicaPonderada();
            asignaturaPonderada.setAsignaturaOferta(asignaturaOfertaAcademica);
            asignaturaPonderada.setPonderacion(PONDERACION_ASIGNATURA_NORMAL);
            listaAsignaturasNormalesPonderadas.add(asignaturaPonderada);
        }
        return listaAsignaturasNormalesPonderadas;
    }

    /**
     * Asigna la ponderación correspondiente a las asignaturas optativas de la oferta académica y devuelve el conjunto resultante.
     * @param listaAsignaturasOptativas Conjunto de asignaturas optativas de la oferta académica.
     * @return Conjunto de asignaturas optativas de la oferta académica con ponderaciones asignadas.
     */
    private ArrayList<AsignaturaOfertaAcademicaPonderada> obtenerAsignaturasPonderadasOptativas(ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOptativas) {
        ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasOptativasPonderadas = new ArrayList<AsignaturaOfertaAcademicaPonderada>();

        for (int i = 0; i < listaAsignaturasOptativas.size(); i++) {
            AsignaturaOfertaAcademica asignaturaOfertaAcademica = listaAsignaturasOptativas.get(i);
            AsignaturaOfertaAcademicaPonderada asignaturaPonderada = new AsignaturaOfertaAcademicaPonderada();
            asignaturaPonderada.setAsignaturaOferta(asignaturaOfertaAcademica);
            asignaturaPonderada.setPonderacion(PONDERACION_ASIGNATURA_OPTATIVA);
            listaAsignaturasOptativasPonderadas.add(asignaturaPonderada);
        }
        return listaAsignaturasOptativasPonderadas;
    }

    /**
     * Asigna ponderaciones a las asignaturas con base en el semestre al cual pertenecen según la retícula académica del programa educativo.
     * @param listaAsignaturasPonderadas Asignaturas ponderadas con base en el semestre al que pertencen.
     */
    private void asignarPonderacionAAsignaturasPorSemestre(ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadas) {

        for (int i = 0; i < listaAsignaturasPonderadas.size(); i++) {
            AsignaturaOfertaAcademicaPonderada asignatura = listaAsignaturasPonderadas.get(i);

            String semestre = asignatura.getAsignaturaOferta().getSemestre();
            int ponderacionPorSemestre = obtenerPonderacionPorSemestre(semestre);

            asignatura.sumarPonderacion(ponderacionPorSemestre);
        }

    }

    /**
     * Calcula la ponderación adecuada para las asignaturas según el semestre indicado.
     * @param semestre Semestre de la asignatura.
     * @return Ponderación adecuada según el semestre.
     */
    private int obtenerPonderacionPorSemestre(String semestre) {

        int ponderacionResultante = 0;

        if(semestre.equals("1")) {         
                ponderacionResultante = 8;
        }
        else if(semestre.equals("2")) {
                ponderacionResultante = 7;
        }
        else if(semestre.equals("3")){
                ponderacionResultante = 6;
        }
        else if(semestre.equals("4")){
                ponderacionResultante = 5;
        }
        else if(semestre.equals("5")){
                ponderacionResultante = 4;
        }
        else if(semestre.equals("6")){
                ponderacionResultante = 3;
        }
        else if(semestre.equals("7")){
                ponderacionResultante = 2;
        }
        else if(semestre.equals("8")){
                ponderacionResultante = 1;
        }        

        return ponderacionResultante;
    }

    /**
     * Asigna la ponderación correspondiente a las asignaturas con base en el cumplimiento de los requerimientos
     * académicos de las asignaturas.
     * @param listaAsignaturasDependientes Conjunto de asignaturas dependientes.
     * @param listaDeTodasLasAsignaturasAprobadas Conjunto de asignaturas aprobadas por el alumno.
     * @param clavePE clave del programa educativo.
     */
    private void asignarPonderacionesConBaseEnLosRequerimientosAcademicos(ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasDependientes, ArrayList<AsignaturaOfertaAcademica> listaDeTodasLasAsignaturasAprobadas, int clavePE) {

        for (int i = 0; i < listaAsignaturasDependientes.size(); i++) {            

            AsignaturaOfertaAcademicaPonderada asignaturaDependiente = listaAsignaturasDependientes.get(i);
            int claveAsignatura = asignaturaDependiente.getAsignaturaOferta().getClaveAsignatura();

            DAORequerimientoAcademico daoRequerimientoAcademico = new DAORequerimientoAcademico();
            RequerimientoAcademico requerimientoAcademico = daoRequerimientoAcademico.obtenerRequerimientoAcademico(claveAsignatura, clavePE);
            
            if(requerimientoAcademico != null) {
                ArrayList<Asignatura> listaAsignaturasRequeridas = requerimientoAcademico.getAsignaturasRequeridas();

                for (int j = 0; j < listaAsignaturasRequeridas.size(); j++) {
                    int contador = 0;

                    Asignatura asignaturaRequerida = listaAsignaturasRequeridas.get(j);
                    int claveAsignaturaRequerida = asignaturaRequerida.getClaveAsignatura();

                    for (int k = 0; k < listaDeTodasLasAsignaturasAprobadas.size(); k++) {
                        AsignaturaOfertaAcademica asignaturaAprobada = listaDeTodasLasAsignaturasAprobadas.get(k);
                        int claveAsignaturaAprobada = asignaturaAprobada.getClaveAsignatura();

                        if (claveAsignaturaAprobada == claveAsignaturaRequerida) {
                            contador++;
                        }
                    }

                    if (contador == 0) {
                        asignaturaDependiente.restarPonderacion(PONDERACION_REQUERIMIENTO_ACADEMICO_NO_CUMPLIDO);
                    }
                }
            }
        }
    }
    
    /**
     * Asigna ponderaciones a las asignaturas con base en la información registrada en las encuestas de hábitos de estudio del alumno
     * y la información registrada del par Asignatura-Profesor por otros alumnos.
     * @param nombreUsuario Nombre de usuario del alumno.
     * @param listaAsignaturasOferta Conjunto de asignaturas de la oferta académica.
     */
    private void asignarPonderacionConBaseEnRespuestasDeEncuestas(String nombreUsuario, ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasOferta) {
        
        ArrayList<PreguntaEHE> listaPreguntasEHE = obtenerListaDePreguntasEHE();
        ArrayList<RelacionRespuestaEHEPreguntaEDD> listaRelacionesPreguntasEncuestas = obtenerRelacionesDePreguntasDeEncuestas(nombreUsuario, listaPreguntasEHE);
        
        for(int i = 0; i < listaAsignaturasOferta.size(); i++) {
            int totalDePonderacion = 0;
            AsignaturaOfertaAcademicaPonderada asignaturaOferta = listaAsignaturasOferta.get(i);
            
            for(int j = 0; j < listaRelacionesPreguntasEncuestas.size(); j++) {
                PreguntaEDD preguntaEDD = listaRelacionesPreguntasEncuestas.get(j).getPreguntaEDD();                
                
                double respuestaPromedioDelParAsignaturaProfesor = obtenerRespuestasPromedioParaElParAsignaturaProfesor(asignaturaOferta, preguntaEDD);
                
                if(respuestaPromedioDelParAsignaturaProfesor != -1) {
                    RespuestaEHE respuestaEHE = listaRelacionesPreguntasEncuestas.get(j).getRespuestaEHE();
                    
                    if(respuestaEHE != null) {
                        int ponderacionDeRespuesta = obtenerPonderacionDeLaAsignatura(respuestaEHE ,respuestaPromedioDelParAsignaturaProfesor);
                    
                        totalDePonderacion += ponderacionDeRespuesta;    
                    }
                }
                else {
                    break;
                }
            }       
            
            double promedioPonderacion = totalDePonderacion / listaRelacionesPreguntasEncuestas.size();
            
            asignaturaOferta.sumarPonderacion(promedioPonderacion); 
        }
        
    }
    
    /**
     * Método para obtener la lista de preguntas de la Encuesta de Hábitos de Estudio.
     * @return ArrayList de instancias de preguntaEHE.
     */
    private ArrayList<PreguntaEHE> obtenerListaDePreguntasEHE() {
        DAOPreguntasEHE daoPreguntasEHE = new DAOPreguntasEHE();
        ArrayList<PreguntaEHE> listaPreguntasEHE = daoPreguntasEHE.buscarTodasLasPreguntasEHE();
        daoPreguntasEHE.cerrarConexionAPreguntasEHE();
        
        return listaPreguntasEHE;
    }
    
    /**
     * Método para obtener las relaciones entre las respuestas de la
     * Encuesta de Hábitos de Estudio y las preguntas de la 
     * Encuesta de Desempeño Docente de la información registrada del alumno.
     * @param nombreUsuario Nombre de usuario que identifica al alumno.
     * @param listaPreguntasEHE Conjunto de preguntas de la encuesta de Hábitos de Estudio.
     * @return Conjunto de relaciones de respuestas de la EHE y preguntas de la EDD.
     */
    private ArrayList<RelacionRespuestaEHEPreguntaEDD> obtenerRelacionesDePreguntasDeEncuestas(String nombreUsuario, ArrayList<PreguntaEHE> listaPreguntasEHE) {
        DAORelacionesPreguntas daoRelacionesPreguntas = new DAORelacionesPreguntas();
        ArrayList<RelacionRespuestaEHEPreguntaEDD> listaRelacionesPreguntasEncuestas = daoRelacionesPreguntas.buscarRelacionesRespuestaEHEPreguntaEDD(nombreUsuario, listaPreguntasEHE);
        daoRelacionesPreguntas.cerrarConexionARelacionesPreguntas();
        
        return listaRelacionesPreguntasEncuestas;
    }
    
    /**
     * Obtiene el promedio de la calificación proporcionada por los alumnos al par Asignatura-Profesor
     * @param asignaturaOferta Asignatura de la oferta académica ponderada.
     * @param preguntaEDD Pregunta de la Encuesta de Desempeño Docente
     * @return Calificación promedio del par Asignatura-Profesor.
     */
    private double obtenerRespuestasPromedioParaElParAsignaturaProfesor(AsignaturaOfertaAcademicaPonderada asignaturaOferta, PreguntaEDD preguntaEDD){
        DAORespuestasEDD daoRespuestasEDD = new DAORespuestasEDD();
        double respuestaPromedioDelParAsignaturaProfesor = daoRespuestasEDD.obtenerRespuestasPromedioParaElParAsignaturaProfesor(asignaturaOferta, preguntaEDD);
        daoRespuestasEDD.cerrarConexionARespuestasEHE();
        
        return respuestaPromedioDelParAsignaturaProfesor;
    }
    
    /**
     * Calcula la ponderación correspondiente para la asignatura con base en la respuesta
     * de la pregunta evaluada de la Encuesta de Hábitos de Estudio y el promedio
     * de calificaciones para la Encuesta de Desempeño Docente en relación a la asignatura evaluada.
     * @param respuestaEHE Respuesta de la Encuesta de Hábitos de estudio
     * @param respuestaPromedioEDDDeLaAsignatura Promedio de calificaciones de la Encuesta de Desempeño Docente para la asignatura evaluada.
     * @return Ponderación de la asignatura.
     */
    private int obtenerPonderacionDeLaAsignatura(RespuestaEHE respuestaEHE, double respuestaPromedioEDDDeLaAsignatura) {
        
        int ponderacion = 0;
        
        int respuestaDeLaPreguntaEHE = respuestaEHE.getRespuesta();
        
        double resultadoOperacion = Math.abs(respuestaDeLaPreguntaEHE - respuestaPromedioEDDDeLaAsignatura);
        
        if(resultadoOperacion == 0) {
            ponderacion = 10;
        }
        else if(resultadoOperacion > 0 && resultadoOperacion < 1) {
            ponderacion = 7;
        }
        else if(resultadoOperacion >= 1 && resultadoOperacion < 2) {
            ponderacion = 4;
        }
        else if(resultadoOperacion >= 2 && resultadoOperacion < 3) {
            ponderacion = -4;
        }
        else if(resultadoOperacion >=3 && resultadoOperacion < 4) {
            ponderacion = -7;
        }
        else if(resultadoOperacion >= 4) {
            ponderacion = -10;
        }
        
        return ponderacion;
    }

    /**
     * Fusiona los arreglos de asignaturas ponderadas.
     * @param listaAsignaturasPonderadasNormalesRecursables Asignaturas recursables ponderadas.
     * @param listaAsignaturasPonderadasNormalesCursablesSinRecursables Asignaturas cursables (sin recuersables) ponderadas.
     * @param listaAsignaturasPonderadasOptativasRecursables Asignaturas optativas recursables.
     * @param listaAsignaturasPonderadasOptativasCursablesSinRecursables Asignaturas optativas cursables (sin recursables) ponderadas.
     * @return Arreglo de asignaturas ponderadas.
     */
    private ArrayList<AsignaturaOfertaAcademicaPonderada> unirArreglosDeAsignaturasPonderadas(ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasNormalesRecursables, ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasNormalesCursablesSinRecursables, ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasOptativasRecursables, ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasOptativasCursablesSinRecursables) {
        ArrayList<AsignaturaOfertaAcademicaPonderada> arregloAsignaturasOrdenadas = new ArrayList<AsignaturaOfertaAcademicaPonderada>();

        arregloAsignaturasOrdenadas.addAll(listaAsignaturasPonderadasNormalesRecursables);
        arregloAsignaturasOrdenadas.addAll(listaAsignaturasPonderadasNormalesCursablesSinRecursables);
        arregloAsignaturasOrdenadas.addAll(listaAsignaturasPonderadasOptativasRecursables);
        arregloAsignaturasOrdenadas.addAll(listaAsignaturasPonderadasOptativasCursablesSinRecursables);

        return arregloAsignaturasOrdenadas;
    }

    /**
     * Fusiona los ArrayList de asignaturas normales recursables y cursables
     * @param listaAsignaturasPonderadasNormalesRecursables Asignaturas normales recursables.
     * @param listaAsignaturasPonderadasNormalesCursablesSinRecursables Asignaturas normales cursables.
     * @return ArrayList de AsignaturaOfertaAcademicaPonderada
     */
    private ArrayList<AsignaturaOfertaAcademicaPonderada> unirArreglosDeAsignaturasPonderadas(ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasNormalesRecursables, ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadasNormalesCursablesSinRecursables) {
        ArrayList<AsignaturaOfertaAcademicaPonderada> arregloAsignaturasOrdenadas = new ArrayList<AsignaturaOfertaAcademicaPonderada>();

        arregloAsignaturasOrdenadas.addAll(listaAsignaturasPonderadasNormalesRecursables);
        arregloAsignaturasOrdenadas.addAll(listaAsignaturasPonderadasNormalesCursablesSinRecursables);

        return arregloAsignaturasOrdenadas;
    }

    /**
     * Invoca la generación de horarios y ordena las opciones obtenidas en orden de mayor a menor conveniencia.
     * @param listaAsignaturasOrdenadas Lista de asignaturas ponderadas de la oferta académica.
     * @param numeroAsignaturasAGenerar Número de asignaturas a considerar por cada opción de horario generada.
     * @return Devuelve la lista de opciones de horario ordenadas de mayor a menor 
     * conveniencia para el alumno de acuerdo con las ponderaciones asignadas a
     * las asignaturas.
     */
    private ArrayList<OpcionHorario> obtenerOpcionesDeHorario(ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasOrdenadas, int numeroAsignaturasAGenerar) {
        System.out.println("Generando combinaciones de horarios");
        ArrayList<OpcionHorario> opcionesDeHorario = obtenerCombinacionesDeOpcionesDeHorario(listaAsignaturasOrdenadas, numeroAsignaturasAGenerar);
        System.out.println("Ordenando opciones de horarios");
        ordenarPorPonderacionOpcionesDeHorario(opcionesDeHorario);

        return opcionesDeHorario;
    }

    /**
     * Genera las opciones de horario para el alumno con base en las ponderaciones asignadas
     * y el número de asignaturas a considerar por cada opción generada.
     * @param listaAsignaturasOrdenadas ArrayList de asignaturas ponderadas de la oferta académica.
     * @param numeroAsignaturasAGenerar Número de asignaturas a considerar por opción de horario generada.
     * @return ArrayList de opciones de horario.
     */
    private ArrayList<OpcionHorario> obtenerCombinacionesDeOpcionesDeHorario(ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasOrdenadas, int numeroAsignaturasAGenerar) {

        Iterador iteradorCombinacion = new Iterador(listaAsignaturasOrdenadas, numeroAsignaturasAGenerar);
        Iterator iterador = iteradorCombinacion.iterator();

        System.out.println("Iniciado " + new java.util.Date().toLocaleString());

        ArrayList<OpcionHorario> opcionesDeHorario = new ArrayList<OpcionHorario>();

        while (iterador.hasNext()) {
            boolean seAgregara = true;
            OpcionHorario opcionHorario = (OpcionHorario) iterador.next();
         
            if(opcionHorario.getListaAsignaturas().size() == numeroAsignaturasAGenerar) {
                
                if(!existeLaMismaAsignaturaEnLaOpcionDeHorario(opcionHorario)) {

                ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturas = opcionHorario.getListaAsignaturas();

                    for (int i = 0; i < listaAsignaturas.size(); i++) {
                        AsignaturaOfertaAcademicaPonderada asignatura1 = listaAsignaturas.get(i);

                        int j = i + 1;

                        if (j < listaAsignaturas.size()) {
                            for (; j < listaAsignaturas.size(); j++) {
                                AsignaturaOfertaAcademicaPonderada asignatura2 = listaAsignaturas.get(j);

                                if (colisionanLasAsignaturas(asignatura1, asignatura2)) {
                                    seAgregara = false;
                                    break;
                                }
                            }
                        }               
                    }
                    if (seAgregara == true) {
                        opcionesDeHorario.add(opcionHorario);
                    }
                }
            }         
        }

        System.out.println("FInalizado " + new java.util.Date().toLocaleString());

        return opcionesDeHorario;

    }
    
    /**
     * Ordena el ArrayList de opciones de horario que se recibe como parámetro.
     * @param opcionesDeHorario ArrayList de opciones de horario.
     */
    private void ordenarPorPonderacionOpcionesDeHorario (ArrayList<OpcionHorario> opcionesDeHorario) {
        Collections.sort(opcionesDeHorario, Collections.reverseOrder(new ComparadorPonderacion()));                 
    }
    
    /**
     * Verifica la existencia de duplicidad de asignaturas en una misma opción de horario.
     * @param opcionHorario Opción de horario a evaluar.
     * @return True si existe duplicidad de asignaturas, False en caso contrario.
     */
    private boolean existeLaMismaAsignaturaEnLaOpcionDeHorario(OpcionHorario opcionHorario) {
        
        ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturas = opcionHorario.getListaAsignaturas();
        
        for(int i = 0; i < listaAsignaturas.size(); i++) {
            
            int j = i + 1;

            if (j < listaAsignaturas.size()) {
                for (; j < listaAsignaturas.size(); j++) {
                    int claveAsignatura1 = listaAsignaturas.get(i).getAsignaturaOferta().getClaveAsignatura();
                    int claveAsignatura2 = listaAsignaturas.get(j).getAsignaturaOferta().getClaveAsignatura();
                    
                    if(claveAsignatura1 == claveAsignatura2) {
                        return true;
                    }
                }
            }
        }
        
        return false;
        
    }

    /**
     * Verifica la compatibilidad de horarios de impartición de clase de las dos asignaturas recibidas.
     * @param asignatura1 Asignatura 1 a verificar.
     * @param asignatura2 Asignatura 2 a verificar.
     * @return True si los horarios de impartición de las clases son incompatibles, False en caso contrario.
     */
    private boolean colisionanLasAsignaturas(AsignaturaOfertaAcademicaPonderada asignatura1, AsignaturaOfertaAcademicaPonderada asignatura2) {

        ArrayList<FragmentoHorario> listaHorariosAsignatura1 = asignatura1.getAsignaturaOferta().getHorario();
        ArrayList<FragmentoHorario> listaHorariosAsignatura2 = asignatura2.getAsignaturaOferta().getHorario();

        for (int i = 0; i < listaHorariosAsignatura1.size(); i++) {
            FragmentoHorario horarioAsignatura1 = listaHorariosAsignatura1.get(i);

            for (int k = 0; k < listaHorariosAsignatura2.size(); k++) {
                FragmentoHorario horarioAsignatura2 = listaHorariosAsignatura2.get(k);

                if (colisionanLosHorarios(horarioAsignatura1, horarioAsignatura2)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Verifica la compatibilidad de dos fragmenstos de horario de asignaturas.
     * @param horarioAsignatura1 Fragmento horario de la asignatura 1.
     * @param horarioAsignatura2 Fragmento horario de la asignatura 2.
     * @return True si los horarios son incompatibles, False en caso contrario.
     */
    private boolean colisionanLosHorarios(FragmentoHorario horarioAsignatura1, FragmentoHorario horarioAsignatura2) {

        String diaAsignatura1 = horarioAsignatura1.getDiaSemana();
        String diaAsignatura2 = horarioAsignatura2.getDiaSemana();

        if (diaAsignatura1.equals(diaAsignatura2)) {

            String horaInicioAsignatura1 = horarioAsignatura1.getHoraInicio();
            String horaFinAsignatura1 = horarioAsignatura1.getHoraFin();
            String horaInicioAsignatura2 = horarioAsignatura2.getHoraInicio();
            String horaFinAsignatura2 = horarioAsignatura2.getHoraFin();

            if (horaInicioAsignatura1.compareTo(horaInicioAsignatura2) == 0) {
                return true;
            } else if (horaInicioAsignatura1.compareTo(horaInicioAsignatura2) > 0) {

                if (horaInicioAsignatura1.compareTo(horaFinAsignatura2) == 0) {
                    return false;
                } else if (horaInicioAsignatura1.compareTo(horaFinAsignatura2) > 0) {
                    return false;
                } else {
                    return true;
                }

            } else {
                if (horaFinAsignatura1.compareTo(horaInicioAsignatura2) == 0) {
                    return false;
                } else if (horaFinAsignatura1.compareTo(horaInicioAsignatura2) > 0) {
                    return true;
                } else {
                    return false;
                }
            }
        } else {
            return false;
        }
    }

    /**
     * Obtiene las claves de las áreas de concentración de las optativas aprobadas por el alumno.
     * @param listaAsignaturasOptativasAprobadas ArrayList de asignaturas optativas aprobadas.
     * @return ArrayList de Integer conteniendo las claves de las área de concentración determinadas.
     */
    
    private ArrayList<Integer> obtenerClavesDeAreasDeConcentracionAprobadas(ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOptativasAprobadas) {
        ArrayList<Integer> listaClavesAreasConcentracionAprobadas = new ArrayList<Integer>();
        
        for(int i = 0; i < listaAsignaturasOptativasAprobadas.size(); i++) {
            AsignaturaOfertaAcademica asignatura = listaAsignaturasOptativasAprobadas.get(i);
            
            int claveAreaConcentracionDeAsignatura = obtenerClaveAreaDeConcentracionDeAsignaturaOptativa(asignatura);
            int contador = 0;
            
            for(int j = 0; j < listaClavesAreasConcentracionAprobadas.size(); j++) {
                int claveAreaConcentracionAprobada = listaClavesAreasConcentracionAprobadas.get(j);
                
                if(claveAreaConcentracionAprobada == claveAreaConcentracionDeAsignatura) {
                    contador++;
                    break;
                }
            }
            
            if(contador == 0) {
                listaClavesAreasConcentracionAprobadas.add(claveAreaConcentracionDeAsignatura); 
            }
        }
        
        return listaClavesAreasConcentracionAprobadas;
    }    
    
    /**
     * Obtiene la clave del área de concentración a la que pertenece la asignatura optativa indicada.
     * @param asignaturaOptativa Asignatura optativa.
     * @return Clave del área de concentración de la asignatura optativa.
     */
    private int obtenerClaveAreaDeConcentracionDeAsignaturaOptativa(AsignaturaOfertaAcademica asignaturaOptativa) {

        int claveAsignatura = asignaturaOptativa.getClaveAsignatura();

        DAOAsignaturaOptativa daoAsignaturaOptativa = new DAOAsignaturaOptativa();
        int claveAreaConcentracion = daoAsignaturaOptativa.obtenerClaveAreaDeConcentracionDeLaAsignatura(claveAsignatura);
        daoAsignaturaOptativa.cerrarConexionAAsignaturaOptativa();

        return claveAreaConcentracion;
    }
    
    /**
     * Determina y asigna la ponderación correspondiente a las asignaturas optativas tomando en cuenta si completarían un área de concentración que ya haya comenzado el alumno.
     * @param listaClavesAreaConcentracionAprobadas Claves de áreas de concentración que ha iniciado el alumno.
     * @param listaAsignaturas Asignaturas optativas a evaluar.
     */
    private void asignarPonderacionAOptativaPorAreaDeConcentracion(ArrayList<Integer> listaClavesAreaConcentracionAprobadas, ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturas) {
        
        for(int i = 0; i < listaAsignaturas.size(); i++) {
            AsignaturaOfertaAcademicaPonderada asignaturaPonderada= listaAsignaturas.get(i);
            AsignaturaOfertaAcademica asignaturaOferta = asignaturaPonderada.getAsignaturaOferta();
            int claveAreaConcentracionDeAsignatura = obtenerClaveAreaDeConcentracionDeAsignaturaOptativa(asignaturaOferta);
            
            for(int j = 0; j < listaClavesAreaConcentracionAprobadas.size(); j++) {
                int claveAreaConcentracionAprobada = listaClavesAreaConcentracionAprobadas.get(j);
                
                if(claveAreaConcentracionAprobada == claveAreaConcentracionDeAsignatura) {
                    asignaturaPonderada.sumarPonderacion(PONDERACION_AREA_CONCENTRACION);
                }
            }
        }
    }
    
    /**
     * Determina el número de asignaturas optativas aprobadas invocando el método size() del ArrayList recibido.
     * @param listaAsignaturasOptativasAprobadas ArrayList de asignaturas optativas aprobadas.
     * @return El número de asignaturas optativas aprobadas.
     */
    private int obtenerNumeroDeAsignaturasOptativasAprobadas(ArrayList<AsignaturaOfertaAcademica> listaAsignaturasOptativasAprobadas) {
        return listaAsignaturasOptativasAprobadas.size();
    }
        
    /**
     * Asigna la ponderación correspondiente a las asignaturas tomando en cuenta
     * el semestre al que pertenecen según la retícula académica del Programa
     * Educativo del alumno y el semestre en el que se encuentra el mismo.
     * @param listaAsignaturasPonderadas Asignaturas a evaluar.
     * @param claveRelacionAlumnoPE Clave de la relación Alumno-Programa Educativo de la Base de Datos.
     */
    private void asignarPonderacionPorSemestreDeAlumno(ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturasPonderadas, int claveRelacionAlumnoPE) {
        DAORelacionAlumnoProgramaEducativo daoRelacionAlumnoPE = new DAORelacionAlumnoProgramaEducativo(); 
        int semestreAlumno = daoRelacionAlumnoPE.obtenerSemestreDelAlumno(claveRelacionAlumnoPE);
        daoRelacionAlumnoPE.cerrarConexionARelacionAlumnoProgramaEducativo();
        
        for(int i = 0; i < listaAsignaturasPonderadas.size(); i++) {
            AsignaturaOfertaAcademicaPonderada asignatura = listaAsignaturasPonderadas.get(i);
            int semestreDeAsignatura = Integer.parseInt(asignatura.getAsignaturaOferta().getSemestre());
            
            if(semestreAlumno == semestreDeAsignatura) {
                asignatura.sumarPonderacion(PONDERACION_SEMESTRE);
            }
        }
    }
    
    /**
     * Formatea la información de las opciones de horario generadas para el alumno
     * para que sean presentadas al alumno.
     * @param opcionesDeHorario Conjunto de opciones de horario generadas.
     * @param out PrintWriter del request utilizado para escribir la respuesta.
     */
    private void imprimirOpcionesDeHorario(ArrayList<OpcionHorario> opcionesDeHorario, PrintWriter out) {
        
        String respuesta = "<h1>OPCIONES DE HORARIO GENERADAS</h1><br/>"
                + "Las opciones de horario son presentadas en orden de mayor a menor conveniencia para el alumno.";
                
        for(int i = 0; (i < opcionesDeHorario.size()) && (i < NUMERO_OPCIONES_HORARIO_A_iMPRIMIR); i++) {
            OpcionHorario opcionDeHorario = opcionesDeHorario.get(i);            
            
            respuesta += "<h3>Opci&oacute;n de horario "+ (i+1) +"</h3>";          

            respuesta += imprimirOpcionDeHorario(opcionDeHorario);
            
            respuesta += "<br/>";
            respuesta += "<br/>";
        }
        
        if(opcionesDeHorario.isEmpty()) {
            out.println("<h1>OPCIONES DE HORARIO GENERADAS</h1><br/>No existen opciones de horario para tu configuración.");
        }
        else {
            out.println(respuesta);
        }
    }
  
    /**
     * Formatea la instancia de OpcionHorario para ser presentada en forma de tabla representando el horario generado.
     * @param opcionHorario Instancia de OpcionHorario a formatear.
     * @return Cadena que representa la opción de horario formateada como tabla.
     */
    private String imprimirOpcionDeHorario(OpcionHorario opcionHorario){
                        
        ArrayList<AsignaturaOfertaAcademicaPonderada> listaAsignaturas = opcionHorario.getListaAsignaturas();
        
        String respuesta = 
                "<table class=\"estiloHorario\" border=\"1\">"
               +"   <th class=\"conHorario\">"
               +"       Asignatura"
               +"   </th>"
               +"   <th class=\"conHorario\">"
               +"       Profesor"
               +"   </th>"
               +"   <th class=\"conHorario\">"
               +"       Lunes"
               +"   </th>"
               +"   <th class=\"conHorario\">"
               +"       Martes"
               +"   </th>"
               +"   <th class=\"conHorario\">"
               +"       Mi&eacute;rcoles"
               +"   </th>"
               +"   <th class=\"conHorario\">"
               +"       Jueves"
               +"   </th>"
               +"   <th class=\"conHorario\">"
               +"       Viernes"
               +"   </th>";
        for(int i = 0; i < listaAsignaturas.size(); i++) {
            AsignaturaOfertaAcademicaPonderada asignatura = listaAsignaturas.get(i);            
            
            respuesta += 
                    "   <tr>"
                   +"       <td class=\"conHorario\">"
                   +"           "+asignatura.getAsignaturaOferta().getNombre()
                   +"       </td>"                   
                   +"       <td class=\"conHorario\">"
                   +"           "+asignatura.getAsignaturaOferta().getMaestro().getNombre()+" "+asignatura.getAsignaturaOferta().getMaestro().getApellido()
                   +"       </td>" 
                    ;
            
            ArrayList<FragmentoHorario> listaHorarios = asignatura.getAsignaturaOferta().getHorario(); 
            
            int contadorLunes = 0;
            for(int j = 0; j < listaHorarios.size() ; j++) {
                FragmentoHorario fragmentoHorario = listaHorarios.get(j);
                
                if(fragmentoHorario.getDiaSemana().equalsIgnoreCase(LUNES)) {
                    respuesta +=
                             "       <td >"
                            +"           Hora: "+fragmentoHorario.getHoraInicio()+" - "+fragmentoHorario.getHoraFin()+"<br/>"
                            +"           Aula: "+fragmentoHorario.getAula()
                            +"       </td>";
                    contadorLunes++;
                }               
            }  
            
            if(contadorLunes == 0) {
                {
                    respuesta +=
                            "       <td>"                          
                           +"       </td>"; 
                }
            }
            
            int contadorMartes = 0;
            for(int j = 0; j < listaHorarios.size() ; j++) {
                FragmentoHorario fragmentoHorario = listaHorarios.get(j);
                
                if(fragmentoHorario.getDiaSemana().equalsIgnoreCase(MARTES)) {
                    respuesta +=
                             "       <td >"
                            +"           Hora: "+fragmentoHorario.getHoraInicio()+" - "+fragmentoHorario.getHoraFin()+"<br/>"
                            +"           Aula: "+fragmentoHorario.getAula()
                            +"       </td>";
                    contadorMartes++;
                }
            }    
            
            if(contadorMartes == 0) {
                {
                    respuesta +=
                            "       <td>"                          
                           +"       </td>"; 
                }
            }
            
            int contadorMiercoles = 0;
            for(int j = 0; j < listaHorarios.size() ; j++) {
                FragmentoHorario fragmentoHorario = listaHorarios.get(j);
                
                if(fragmentoHorario.getDiaSemana().equalsIgnoreCase(MIERCOLES)) {
                    respuesta +=
                             "       <td>"
                            +"           Hora: "+fragmentoHorario.getHoraInicio()+" - "+fragmentoHorario.getHoraFin()+"<br/>"
                            +"           Aula: "+fragmentoHorario.getAula()
                            +"       </td>";
                    contadorMiercoles++;
                }
            }     
            
            if(contadorMiercoles == 0) {
                {
                    respuesta +=
                            "       <td>"                          
                           +"       </td>"; 
                }
            }
            
            int contadorJueves = 0;
            for(int j = 0; j < listaHorarios.size() ; j++) {
                FragmentoHorario fragmentoHorario = listaHorarios.get(j);
                
                if(fragmentoHorario.getDiaSemana().equalsIgnoreCase(JUEVES)) {
                    respuesta +=
                             "       <td>"
                            +"           Hora: "+fragmentoHorario.getHoraInicio()+" - "+fragmentoHorario.getHoraFin()+"<br/>"
                            +"           Aula: "+fragmentoHorario.getAula()
                            +"       </td>";
                    contadorJueves++;
                }
            }      
            
            if(contadorJueves == 0) {
                {
                    respuesta +=
                            "       <td>"                          
                           +"       </td>"; 
                }
            }
            
            int contadorViernes = 0;
            for(int j = 0; j < listaHorarios.size() ; j++) {
                FragmentoHorario fragmentoHorario = listaHorarios.get(j);
                
                if(fragmentoHorario.getDiaSemana().equalsIgnoreCase(VIERNES)) {
                    respuesta +=
                             "       <td>"
                            +"           Hora: "+fragmentoHorario.getHoraInicio()+" - "+fragmentoHorario.getHoraFin()+"<br/>"
                            +"           Aula: "+fragmentoHorario.getAula()
                            +"       </td>";
                    contadorViernes++;
                }
            }     
            
            if(contadorViernes == 0) {
                {
                    respuesta +=
                            "       <td>"                          
                           +"       </td>"; 
                }
            }
            
            respuesta += "</tr>";
        }
        respuesta += "</table>";
        
        return respuesta;
    }
    
    private final int ESTADO_CURSABLE_SIN_RECURSABLE = -1;
    private final int ESTADO_APROBADA = 0;//Asignatura que el alumno ya curso y Aprobo
    private final int ESTADO_RECURSABLE = 1;//Asignatura que el alumno ya curso y reprobo 
    private final int ESTADO_NO_CURSABLE = 2;//Asignatura que el alumno ya recurso y reprobo
    private final int PONDERACION_ASIGNATURA_RECURSABLE = 15;
    private final int PONDERACION_ASIGNATURA_NORMAL = 5;
    private final int PONDERACION_ASIGNATURA_OPTATIVA = 2;  
    private final int PONDERACION_REQUERIMIENTO_ACADEMICO_NO_CUMPLIDO = 3;
    private final int PONDERACION_AREA_CONCENTRACION = 3;
    private final int PONDERACION_SEMESTRE = 1;
    private final String LUNES = "LUNES";
    private final String MARTES = "MARTES";
    private final String MIERCOLES = "MIÉRCOLES";
    private final String JUEVES = "JUEVES";
    private final String VIERNES = "VIERNES";    
    private final String TURNO_MATUTINO = "Matutino"; 
    private final String TURNO_VESPERTINO = "Vespertino"; 
    private final String TURNO_MEZCLADO = "Ambos"; 
    private final int NUMERO_OPCIONES_HORARIO_A_iMPRIMIR = 20;

    
}
